Javascript 中的物件有兩個主要的目的
這部分 BuckleScript 的物件也是一樣的
直到最近 Javascript
才終於有正確的支援 Map
物件使用上和 Map
可以是一樣的
前提是
這時候可以利用 Js.Dict
來綁定這個 Javascript 物件
你可以就像 Javascript 中的 Object 來做操作
Js.Dict.keys
來取得所有的鍵值
Js.Divt.values
來取得所有的值
let myMap = Js.Dict.empty();
Js.Dict.set(myMap, "Allison", 10);
[@bs.val] external studentAge: Js.Dict.t(int) = "student";
switch (Js.Dict.get(studentAge, "Joe")) {
| None => Js.log("Joe can't be found")
| Some(age) => Js.log("Joe is " ++ string_of_int(age));
};
轉譯後的結果
'use strict';
var myMap = { };
myMap["Allison"] = 10;
var match = student["Joe"];
if (match !== undefined) {
console.log("Joe is " + String(match));
} else {
console.log("Joe can't be found");
}
你可以看到其實 Js.Dict
是基於 Javascript 的物件
整個 API 都沒有使用到 external
所以在編譯之後 整個 API 都會消失
編譯結果中你也找不到 Dict
的相關字
這很便於你將 Js 檔案轉為 BuckleScript 檔案
如果你的 Js 物件:
這時候大多數的程式語言中 你都可以使用 Record
{
"John": 10,
"Allison": 20,
"Jimmy": 15
}
和
{
name: "John",
age: 10,
job: "CEO"
}
的不同
前者會使用 雜湊表
的型態來處理
後者會使用 Record
來處理
而在 BuckleScript 中要使用 bs.deriving abstract
功能
後者則會這樣寫
[@bs.deriving abstract]
type person = {
name: string,
age: int,
job: string
};
[@bs.val] external john: person = "john";
note person
並不是一個 Record
的型別
他只是看起來像是 Record
的型別
而且可以使用 Record
的形態檢查
bs.deriving abstract
就是在註解將 person
轉為 抽象型別
上述的抽象型別因為不是 Record
型別
所以不能直接使用型態的方式來建立
[@bs.deriving abstract]
type person = {
name: string,
age: int,
job: string,
};
let joe = person(~name="Joe", ~age=20, ~job="teacher")
轉譯後的結果
// Generated by BUCKLESCRIPT VERSION 4.0.5, PLEASE EDIT WITH CARE
'use strict';
var joe = {
name: "Joe",
age: 20,
job: "teacher"
};
exports.joe = joe;
/* No side effect */
結果對於運行並不會有成本
有時候在綁定 JS object 的時候,欄位名稱在 BuckleScript/Reason 中是無效的名稱
有兩種範例 一個是 {type: "foo"}
(BS/Reason 中的保留字) 和 {"aria-checked": true}
可以選擇另一個有效的名稱 使用 @bs.as
來規避這個問題
[@bs.deriving abstract]
type data = {
[@bs.as "type"] type_: string,
[@bs.as "aria-checked"] ariaLabel: string
};
let d = data(~type_="message", ~ariaLabel="hello");
輸出會是
'use strict';
var d = {
type: "message",
"aria-checked": "hello"
};
可以建立可忽略的 欄位
[[@bs.deriving abstract]
type person = {
[@bs.optional] name: string,
age: int,
job: string
};
let joe = person(~age=19, ~job="sleep", ());
Js.log(joe);
編譯後會是
var Js_primitive = require("bs-platform/lib/js/js_primitive.js");
function joe(param) {
return (function (param$1) {
var prim = param;
var prim$1 = 19;
var prim$2 = "sleep";
var tmp = {
age: prim$1,
job: prim$2
};
if (prim !== undefined) {
tmp.name = Js_primitive.valFromOption(prim);
}
return tmp;
});
}
console.log(joe);
可以看到 name
會是可選的,若是沒有也不會造成錯誤
note:bs.optional 只是將 name 改為可選,但是如果輸入 Option(string) 不會有作用
當你使用 bs.deriving abstract
隱含了 recode 的型態,你無法使用 joe.age
這樣的方式來取得值
原生會提供 getter
和 setter
來完成這個
每一個 bs.deriving abstract
欄位 都會有一個 getter function
在上面的範例中會有三個 getter function
,nameGet
, ageGet
, jobGet
他們會取得 person
的值並分別回傳 string
, int
, string
[@bs.deriving abstract]
type person = {
[@bs.optional] name: string,
age: int,
job: string,
};
let joe = person(~age=20, ~job="teacher", ());
let twenty = ageGet(joe);
Js.log(twenty);
/**
也可以寫成這樣
joe-> ageGet-> Js.log
**/
至於改值則是
[@bs.deriving abstract]
type person = {
[@bs.optional] name: string,
mutable age: int,
job: string,
};
let joe = person(~age=20, ~job="teacher", ());
joe-> ageGet-> Js.log; /* 20 */
joe-> ageSet(21)-> Js.log;
joe-> ageGet-> Js.log; /* 21 */
note: 要記得將需要修改的參數前面加上 mutable
可以附加任何function
在類型上(任何類型的 record 不僅止於 @bs.deriving abtract
)
可以參閱 Object Method
這部分之後會再討埨
Test.re
[@bs.deriving abstract]
type cord = {
[@bs.optional] mutable x: int,
y: int,
};
Test.rei
[@bs.deriving abstract]
type cord = {
[@bs.optional] x: int,
y: int,
};
如果上述的 Object 並不符合您的需求
也有另外一種方式可以綁定 Javascript Object
有幾個前提
type
Ex: 你的類型希望可以接受所有含有 age
這個參數的物件,而不是只有特定參數的物件
note: 不能使用普通的 Reason/OCaml 的物件類型,如下
type person = {
.
name: string,
age: int,
job: string
};
你仍然可以宣告這個類型
但是無法編譯成功
因為 Reason/OCaml
的對象工作方式是不一樣的
BuckleScript 利用 Js.t
來做包裝類型
以便控制和追蹤可以編譯成 Javascript 的對象子集合
type person = Js.t({
.
name: string,
age: int,
job: string
});
let john = {"name": "john", "age": 11};
john##name -> Js.log;
從現在開始 BuckleScript 都會以 Js.t
來消除跟 一般物件 和 Javascript 物件的歧義
因為 Reason 有包覆一層語法糖 會將 Js.t({. name: string})
轉為 {. "name": string}
要取得值的話方法為 ##
範例如下
type person = Js.t({
.
name: string,
age: int,
job: string
});
let john = {
"name": "john",
"age": 11,
"job": "development"
};
let johnName = john##name;
明天再來談談 Some
和 Option
的問題